package cn.ahaoweb.drule.core;

import cn.ahaoweb.drule.core.configuration.Configuration;
import cn.ahaoweb.drule.core.configuration.Option;
import cn.ahaoweb.drule.core.exception.DruleRuleServiceException;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Name: 规则服务调度器（统一调度器）
 * @Description: 为了避免所有管理RuleService的代码分散调各处，本调度器适用于统一管理。
 * @Author: wangzhonghao
 * @Date: 2025/3/7 14:40
 */
public class RuleServiceScheduler extends RuleServiceFactory {

    // 配置信息
    protected Configuration config;

    /**
     * 用于存储所有规则服务会话执行
     * <pre>
     *     k -> 服务实例的唯一标识符
     *     v -> 服务实例
     * </pre>
     */
    protected Map<String, RuleSession> sessions = new HashMap<>();

    /**
     * 规则服务构造器
     * <pre>
     *  根据标识符获取规则服务RuleService，当配置了该服务构造器ruleServiceConstructor 之后，无需提前构造规则服务RuleService并添加至调度器RuleServiceScheduler中，将会自动通过该构造器构建/获取。
     *  <p color=red>注意：这种方式不适用于{@link RuleServiceScheduler#execute(Object)}</p>
     * </pre>
     */
    protected RuleServiceConstructor ruleServiceConstructor;

    public RuleServiceScheduler() {
    }

    public RuleServiceScheduler(RuleServiceConstructor ruleServiceConstructor) {
        this.ruleServiceConstructor = ruleServiceConstructor;
    }

    public void setRuleServiceConstructor(RuleServiceConstructor ruleServiceConstructor) {
        this.ruleServiceConstructor = ruleServiceConstructor;
    }

    /**
     * 用于获取规则服务。
     *
     * 如果在父类中未找到对应的规则服务，则通过当前类中的逻辑进行获取。
     * 首先尝试获取一个重入锁，确保线程安全。然后，使用规则服务构造器（ruleServiceConstructor）来获取新的规则服务实例，并将其添加到服务集合中。
     * 同时，通过规则服务构造器获取事实类选择器，并与服务实例一起添加到服务集合中。
     *
     * @param identifier 规则服务的标识符
     * @return 返回对应的RuleService实例，如果未找到则返回null
     */

    @Override
    public RuleService getRuleService(String identifier) {
        RuleService ruleService = super.getRuleService(identifier);
        if (ruleService == null){
            ReentrantLock lock = getLock(identifier);
            try {
                lock.lock();
                if ( (ruleService = super.getRuleService(identifier)) == null){
                    ruleService = ruleServiceConstructor.getRuleService(identifier);
                    addService(identifier, ruleService);
                }
            } finally {
                lock.unlock();
            }
        }
        return ruleService;
    }

    /**
     * 根据标识符获取规则服务会话。
     *
     * @param identifier
     * @return
     */
    public RuleSession getRuleSession(String identifier) {
        RuleSession ruleSession = sessions.get(identifier);
        if (ruleSession == null) {
            RuleService ruleService = getRuleService(identifier);
            ruleSession = ruleService.createSession();
            sessions.put(identifier,ruleSession);
        }
        return ruleSession;
    }

    public RuleServiceScheduler addConfig(String name, String value) {
        config.setProperty(name, value);
        return this;
    }

    public RuleServiceScheduler addConfig(Option option) {
        config.setProperty(option);
        return this;
    }

    /**
     * 注意通过此方式调用必须保证满足以下条件
     * <pre>
     *     1.服务实例和入参类型一一对应，即一个入参类型对应一个服务实例；
     *     2.在添加规则服务 RuleService 时事实对象类型选择器不能为空，必须设置{@link RuleService#factClassSelector(FactClassSelector)}
     *     3.支持ruleServiceConstructor动态获取，需要实现 {@link cn.ahaoweb.drule.core.RuleServiceConstructor#getIdentifier} 方法。
     * </pre>
     * @param in 入参对象
     * @return
     */
    public FactInfo execute(Object in){
        Class<?> inClass = in.getClass();
        String identifier = inClassSelectors.get(inClass);
        if (Objects.isNull(identifier)){
            if (ruleServiceConstructor != null){
                identifier = ruleServiceConstructor.getIdentifier(inClass);
            } else {
                throw new DruleRuleServiceException("未配置规则服务构造器");
            }
            if (Objects.isNull(identifier)){
                throw new NullPointerException("不存在入参类型 "+inClass.getCanonicalName()+" 对应的规则标识符");
            } else {
                addService(ruleServiceConstructor.getRuleService(identifier));
            }
        }
        return execute(identifier,in);
    }

    /**
     * 注意通过此方式调用必须保证满足以下条件
     * <pre>
     *     1.在添加规则服务 RuleService 时事实对象类型选择器不能为空，必须设置{@link RuleService#factClassSelector(FactClassSelector)}
     * </pre>
     * @param identifier 标识符
     * @param in 入参对象
     * @return
     */
    public FactInfo execute(String identifier, Object in){
        RuleService ruleService = getRuleService(identifier);
        return execute(identifier,FactInfo.FactInfoBuilder
                .builder()
                .byInput(in)
                .outputClass(ruleService.getFactClassSelector().outType())
                .build());
    }

    public FactInfo execute(String identifier, Object in, Object out) {
        return execute(identifier, FactInfo.FactInfoBuilder
                .builder()
                .byInput(in)
                .byOutput(out)
                .build());
    }

    /**
     * 根据规则服务的唯一标识符选择对应的规则服务实例执行
     *
     * @param identifier
     * @param factInfo
     * @return
     */
    public FactInfo execute(String identifier, FactInfo factInfo) {
        RuleSession ruleSession = getRuleSession(identifier);
        ruleSession.execute(factInfo);
        return factInfo;
    }

}
